//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<CColorBitmap.cpp>
///	@path	~/src/gui/image/
///	@date	2008/06/27
///	@desc	.

#define _FULLAPI
#include "config/config.h"

#include "gui/image/CColorBitmap.h"

namespace xeres {

	namespace {
		static const int COMPONENTS[] = { 1 , 3 , 4 , 4 };
	}

	IMPL_IOBJECT_CLASS( CColorBitmap );

	// CColorBitmap
	CColorBitmap::CColorBitmap( size_t width , size_t height , ColorType type , const byte * src )
		: m_width( width ) , m_height( height )
		, m_type( type )
	{
		m_pitch = m_width * COMPONENTS[ m_type ];
		m_imageSize = m_pitch * m_height;

		assert( m_width > 0 && m_height > 0 );
		switch( m_type )
		{
		case COLOR_MONO:
			break;
		case COLOR_RGB:
			break;
		case COLOR_RGBA:
		case COLOR_ARGB:
			break;
		default:
			FATAL_ABORT( _S("CColorBitmap::CTOR: Unsupported color type.") );
		}
		m_bits = AllocateImage( m_imageSize );

		// Copy!
		memcpy( m_bits , src , m_imageSize );
	}
	// CColorBitmap
	CColorBitmap::CColorBitmap( Gdiplus::Bitmap * bitmap )
		: m_width( bitmap->GetWidth() ) , m_height( bitmap->GetHeight() )
	{
		m_type = GetPixelFormatType( bitmap );
		m_pitch = m_width * COMPONENTS[ m_type ];
		m_imageSize = m_pitch * m_height;

		m_bits = AllocateImage( m_imageSize );
		MakeBitmap( bitmap , m_bits );
	}
	// CColorBitmap
	CColorBitmap::CColorBitmap( size_t width , size_t height , ColorType type )
	: m_width( width ) , m_height( height )
		, m_type( type )
	{
		m_pitch = m_width * COMPONENTS[ m_type ];
		m_imageSize = m_pitch * m_height;

		assert( m_width > 0 && m_height > 0 );
		switch( m_type )
		{
		case COLOR_MONO:
			break;
		case COLOR_RGB:
			break;
		case COLOR_RGBA:
		case COLOR_ARGB:
			break;
		default:
			FATAL_ABORT( _S("CColorBitmap::CTOR: Unsupported color type.") );
		}
		m_bits = NULL;
	}
	// CColorBitmap
	CColorBitmap::CColorBitmap( Gdiplus::Bitmap& bitmap )
		: m_width( bitmap.GetWidth() ) , m_height( bitmap.GetHeight() )
	{
		m_type = GetPixelFormatType( &bitmap );
		m_pitch = m_width * COMPONENTS[ m_type ];
		m_imageSize = m_pitch * m_height;

		m_bits = NULL;
	}
	// ~CColorBitmap
	CColorBitmap::~CColorBitmap( void )
	{
		assert( m_bits != 0 );
		Deallocate( m_bits );
	}
	// GetWidth
	size_t CColorBitmap::GetWidth( void )
	{
		return m_width;
	}
	// GetHeight
	size_t CColorBitmap::GetHeight( void )
	{
		return m_height;
	}
	// GetSize
	Pair<int> CColorBitmap::GetSize( void )
	{
		return Pair<int>( m_width , m_height );
	}
	// GetFrame
	int CColorBitmap::GetFrame( void )
	{
		return 0;
	}
	// GetDelay
	float CColorBitmap::GetDelay( int frame )
	{
		return -1;
	}
	// GetStorageSize
	size_t CColorBitmap::GetStorageSize( void )
	{
		return m_imageSize;
	}
	// GetColorType
	ColorType CColorBitmap::GetColorType( void )
	{
		return m_type;
	}
	// GetPixel
	RGBA CColorBitmap::GetPixel( const Pair<int>& point , int frame )
	{
		if( frame != 0 )
		{
			TRACE_WARNING( _S("CColorBitmap::GetSampler: No sub frame in bitmap.") );
		}
		return GetPixelBase( point , m_bits );
	}
	// GetPixel
	RGBA CColorBitmap::GetPixelBase( const Pair<int>& point , byte * base )
	{
		XS_ASSERT( point.x < (int)m_width && point.y < (int)m_height );
		byte * pixel = base + ( point.x + m_width * point.y ) * COMPONENTS[ m_type ];
		switch( m_type )
		{
		case COLOR_MONO:
			return RGBA( *pixel , *pixel , *pixel , 255 );
		case COLOR_RGB:
			return RGBA( pixel[2] , pixel[1] , pixel[0] , 255 );
		case COLOR_RGBA:
			return *(RGBA*)pixel;
		case COLOR_ARGB:
			return to_rgba( *(ARGB*)pixel );
		}
		XS_ASSERT(false);
		__assume(false);
	}
	// GetPixelOffset
	byte * CColorBitmap::GetPixelOffset( const Pair<int>& point , byte * offset )
	{
		XS_ASSERT( point.x < (int)m_width && point.y < (int)m_height );
		return offset + ( point.x + m_width * point.y ) * COMPONENTS[ m_type ];
	}
	// GetSampler
	void CColorBitmap::GetSampler( const Pair<int>& point , Sampler& sampler , int frame )
	{
		if( frame != 0 )
		{
			TRACE_WARNING( _S("CColorBitmap::GetSampler: No sub frame in bitmap.") );
		}
		XS_ASSERT( point.x < (int)m_width && point.y < (int)m_height );
		sampler.m_bits = GetPixelOffset( point , m_bits );
		sampler.m_pitch = m_pitch;
		sampler.m_type = m_type;
	}
	// GetPixelFormatType
	ColorType CColorBitmap::GetPixelFormatType( Gdiplus::Bitmap * bitmap )
	{
		using namespace Gdiplus;
		switch( bitmap->GetPixelFormat() )
		{
		case PixelFormat1bppIndexed:
		case PixelFormat16bppGrayScale:
			return COLOR_MONO;
		case PixelFormat4bppIndexed:
		case PixelFormat8bppIndexed:
		case PixelFormat16bppRGB555:
		case PixelFormat16bppRGB565:
		case PixelFormat24bppRGB:
		case PixelFormat32bppRGB:
		case PixelFormat48bppRGB:
			return COLOR_RGB;
		case PixelFormat16bppARGB1555:
		case PixelFormat32bppARGB:
		case PixelFormat32bppPARGB:
		case PixelFormat64bppARGB:
		case PixelFormat64bppPARGB:
			return COLOR_ARGB;
		}
		XS_ASSERT(false);
		__assume(false);
	}
	// MakeBitmap
	void CColorBitmap::MakeBitmap( Gdiplus::Bitmap * bitmap , byte * dst )
	{
		Gdiplus::Color cl;
		switch( m_type )
		{
		case COLOR_MONO:
			for( size_t y = 0 ; y < m_height ; ++y )
			{
				byte * pixel = dst + m_width * y;
				for( size_t x = 0 ; x < m_width ; ++x , ++pixel )
				{
					bitmap->GetPixel( (int)x , (int)y , &cl );
					*pixel = cl.GetR();
				}
			}
			break;
		case COLOR_RGB:
			for( size_t y = 0 ; y < m_height ; ++y )
			{
				byte * pixel = dst + m_width * y * 3;
				for( size_t x = 0 ; x < m_width ; ++x , pixel += 3 )
				{
					bitmap->GetPixel( (int)x , (int)y , &cl );
					pixel[0] = cl.GetB();
					pixel[1] = cl.GetG();
					pixel[2] = cl.GetR();
				}
			}
			break;
		case COLOR_ARGB:
			for( size_t y = 0 ; y < m_height ; ++y )
			{
				ARGB * pixel = (ARGB*)( dst + m_width * y * 4 );
				for( size_t x = 0 ; x < m_width ; ++x , ++pixel )
				{
					bitmap->GetPixel( (int)x , (int)y , &cl );
					*pixel = ARGB( cl.GetValue() );
				}
			}
		}
	}
	// AllocateImage
	byte * CColorBitmap::AllocateImage( size_t image_size )
	{
		return (byte*)_aligned_malloc( ALIGNOF( image_size , ALIGN_SIZE ) , ALIGN_SIZE );
	}
	// Deallocate
	void CColorBitmap::Deallocate( byte * image )
	{
		_aligned_free( image );
	}
	
} // namespace xeres
